home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 7: Sunsite / Linux Cubed Series 7 - Sunsite Vol 1.iso / system / news / inn1.000 / inn1.4sec-linux-src.tar / inn / innd / chan.c < prev    next >
C/C++ Source or Header  |  1993-03-18  |  19KB  |  855 lines

  1. /*  $Revision: 1.30 $
  2. **
  3. **  I/O channel (and buffer) processing.
  4. */
  5. #include "innd.h"
  6.  
  7.  
  8. STATIC FDSET    RCHANmask;
  9. STATIC FDSET    SCHANmask;
  10. STATIC FDSET    WCHANmask;
  11. STATIC int    SCHANcount;
  12. STATIC int    CHANlastfd;
  13. STATIC int    CHANlastsleepfd;
  14. STATIC int    CHANccfd;
  15. STATIC int    CHANtablesize;
  16. STATIC CHANNEL    *CHANtable;
  17. STATIC CHANNEL    *CHANcc;
  18. STATIC CHANNEL    CHANnull = { CTfree, CSerror, -1 };
  19.  
  20.  
  21. /*
  22. **  Set a buffer's contents, ignoring anything that might have
  23. **  been there.
  24. */
  25. void
  26. BUFFset(bp, p, length)
  27.     register BUFFER    *bp;
  28.     register char    *p;
  29.     register int    length;
  30. {
  31.     register char    *dest;
  32.  
  33.     if ((bp->Left = length) != 0) {
  34.     /* Need more space? */
  35.     if (bp->Size < length) {
  36.         bp->Size = GROW_AMOUNT(length);
  37.         RENEW(bp->Data, char, bp->Size);
  38.     }
  39.  
  40.     if (length > MEMCPY_THRESHOLD)
  41.         (void)memcpy((POINTER)bp->Data, (POINTER)p, (SIZE_T)length);
  42.     else {
  43.         for (dest = bp->Data, length++; --length > 0; )
  44.         *dest++ = *p++;
  45.     }
  46.     }
  47.     bp->Used = 0;
  48. }
  49.  
  50.  
  51. /*
  52. **  Initialize all the I/O channels.
  53. */
  54. void
  55. CHANsetup(i)
  56.     register int    i;
  57. {
  58.     register CHANNEL    *cp;
  59.  
  60.     FD_ZERO(&RCHANmask);
  61.     FD_ZERO(&SCHANmask);
  62.     FD_ZERO(&WCHANmask);
  63.     if (CHANtable)
  64.     DISPOSE(CHANtable);
  65.     CHANtablesize = i;
  66.     CHANtable = NEW(CHANNEL, CHANtablesize);
  67.     (void)memset((POINTER)CHANtable, 0,
  68.         (SIZE_T)(CHANtablesize * sizeof *CHANtable));
  69.     CHANnull.NextLog = CHANNEL_INACTIVE_TIME;
  70.     CHANnull.Address.s_addr = MyAddress.s_addr;
  71.     for (cp = CHANtable; --i >= 0; cp++)
  72.     *cp = CHANnull;
  73. }
  74.  
  75.  
  76. /*
  77. **  Create a channel from a descriptor.
  78. */
  79. CHANNEL *
  80. CHANcreate(fd, Type, State, Reader, WriteDone)
  81.     int            fd;
  82.     CHANNELTYPE        Type;
  83.     CHANNELSTATE    State;
  84.     FUNCPTR        Reader;
  85.     FUNCPTR        WriteDone;
  86. {
  87.     register CHANNEL    *cp;
  88.     BUFFER        in;
  89.     BUFFER        out;
  90.  
  91.     cp = &CHANtable[fd];
  92.  
  93.     /* Don't overwrite the buffers with CHANnull. */
  94.     in = cp->In;
  95.     if (in.Size == 0) {
  96.     in.Size = START_BUFF_SIZE;
  97.     in.Data = NEW(char, in.Size);
  98.     }
  99.     in.Used = 0;
  100.     in.Left = in.Size;
  101.     out = cp->Out;
  102.     if (out.Size == 0) {
  103.     out.Size = SMBUF;
  104.     out.Data = NEW(char, out.Size);
  105.     }
  106.     out.Used = 0;
  107.     out.Left = 0;
  108.  
  109.     /* Set up the channel's info. */
  110.     *cp = CHANnull;
  111.     cp->fd = fd;
  112.     cp->Type = Type;
  113.     cp->State = State;
  114.     cp->Reader = Reader;
  115.     cp->WriteDone = WriteDone;
  116.     cp->Started = cp->LastActive = Now.time;
  117.     cp->In = in;
  118.     cp->Out = out;
  119.     cp->Tracing = Tracing;
  120.  
  121.     /* Make the descriptor close-on-exec and non-blocking. */
  122.     CloseOnExec(fd, TRUE);
  123. #if    defined(ENOTSOCK)
  124.     if (SetNonBlocking(fd, TRUE) < 0 && errno != ENOTSOCK)
  125.     syslog(L_ERROR, "%s cant nonblock %d %m", LogName, fd);
  126. #else
  127.     if (SetNonBlocking(fd, TRUE) < 0)
  128.     syslog(L_ERROR, "%s cant nonblock %d %m", LogName, fd);
  129. #endif    /* defined(ENOTSOCK) */
  130.  
  131.     /* Note control channel, for efficiency. */
  132.     if (Type == CTcontrol) {
  133.     CHANcc = cp;
  134.     CHANccfd = fd;
  135.     }
  136.     return cp;
  137. }
  138.  
  139.  
  140. /*
  141. **  Start tracing a channel.
  142. */
  143. void
  144. CHANtracing(cp, Flag)
  145.     register CHANNEL    *cp;
  146.     BOOL        Flag;
  147. {
  148.     char        *p;
  149.  
  150.     p = CHANname(cp);
  151.     syslog(L_NOTICE, "%s trace %s", p, Flag ? "on" : "off");
  152.     cp->Tracing = Flag;
  153.     if (Flag) {
  154.     syslog(L_NOTICE, "%s trace badwrites %d blockwrites %d badreads %d",
  155.         p, cp->BadWrites, cp->BadReads, cp->BlockedWrites);
  156.     syslog(L_NOTICE, "%s trace address %s lastactive %ld nextlod %ld",
  157.         p, inet_ntoa(cp->Address), cp->LastActive, cp->NextLog);
  158.     if (FD_ISSET(cp->fd, &SCHANmask))
  159.         syslog(L_NOTICE, "%s trace sleeping %ld 0x%x",
  160.         p, (long)cp->Waketime, cp->Waker);
  161.     if (FD_ISSET(cp->fd, &RCHANmask))
  162.         syslog(L_NOTICE, "%s trace reading %d %s",
  163.         p, cp->In.Used, MaxLength(cp->In.Data, cp->In.Data));
  164.     if (FD_ISSET(cp->fd, &WCHANmask))
  165.         syslog(L_NOTICE, "%s trace writing %d %s",
  166.         p, cp->Out.Left, MaxLength(cp->Out.Data, cp->Out.Data));
  167.     }
  168. }
  169.  
  170.  
  171. /*
  172. **  Close a channel.
  173. */
  174. void
  175. CHANclose(cp, name)
  176.     register CHANNEL    *cp;
  177.     char        *name;
  178. {
  179.     if (cp->Type == CTfree)
  180.     syslog(L_ERROR, "%s internal closing free channel", name);
  181.     else {
  182.     if (cp->Type == CTnntp)
  183.         syslog(L_NOTICE,
  184.         "%s closed seconds %ld accepted %ld refused %ld rejected %ld",
  185.         name, (long)(Now.time - cp->Started),
  186.         cp->Received, cp->Refused, cp->Rejected);
  187.     else
  188.         syslog(L_NOTICE, "%s closed", name);
  189.     WCHANremove(cp);
  190.     RCHANremove(cp);
  191.     SCHANremove(cp);
  192.     if (cp->Argument != NULL)
  193.         /* Set to NULL below. */
  194.         DISPOSE(cp->Argument);
  195.     if (cp->fd >= 0 && close(cp->fd) < 0)
  196.         syslog(L_ERROR, "%s cant close %s %m", LogName, name);
  197.     }
  198.  
  199.     /* Mark it unused. */
  200.     cp->Type = CTfree;
  201.     cp->State = CSerror;
  202.     cp->fd = -1;
  203.     cp->Argument = NULL;
  204.  
  205.     /* Free the buffers if they got big. */
  206.     if (cp->In.Size > BIG_BUFFER) {
  207.     cp->In.Size = 0;
  208.     DISPOSE(cp->In.Data);
  209.     }
  210.     if (cp->Out.Size > BIG_BUFFER) {
  211.     cp->Out.Size = 0;
  212.     DISPOSE(cp->Out.Data);
  213.     }
  214. }
  215.  
  216.  
  217. /*
  218. **  Return a printable name for the channel.
  219. */
  220. char *
  221. CHANname(cp)
  222.     register CHANNEL    *cp;
  223. {
  224.     static char        buff[SMBUF];
  225.     register int    i;
  226.     register SITE    *sp;
  227.     STRING        p;
  228.     PID_T        pid;
  229.  
  230.     switch (cp->Type) {
  231.     default:
  232.     (void)sprintf(buff, "?%d(#%d@%d)?", cp->Type, cp->fd, cp - CHANtable);
  233.     break;
  234.     case CTany:
  235.     (void)sprintf(buff, "any:%d", cp->fd);
  236.     break;
  237.     case CTfree:
  238.     (void)sprintf(buff, "free:%d", cp->fd);
  239.     break;
  240.     case CTremconn:
  241.     (void)sprintf(buff, "remconn:%d", cp->fd);
  242.     break;
  243.     case CTnntp:
  244.     (void)sprintf(buff, "%s:%d",
  245.         cp->Address.s_addr == 0 ? "localhost" : RChostname(cp),
  246.         cp->fd);
  247.     break;
  248.     case CTlocalconn:
  249.     (void)sprintf(buff, "localconn:%d", cp->fd);
  250.     break;
  251.     case CTcontrol:
  252.     (void)sprintf(buff, "control:%d", cp->fd);
  253.     break;
  254.     case CTexploder:
  255.     case CTfile:
  256.     case CTprocess:
  257.     /* Find the site that has this channel. */
  258.     for (p = "?", i = nSites, sp = Sites, pid = 0; --i >= 0; sp++)
  259.         if (sp->Channel == cp) {
  260.         p = sp->Name;
  261.         if (cp->Type != CTfile)
  262.             pid = sp->pid;
  263.         break;
  264.         }
  265.     if (pid == 0)
  266.         (void)sprintf(buff, "%s:%d:%s",
  267.             MaxLength(p, p), cp->fd,
  268.             cp->Type == CTfile ? "file" : "proc");
  269.     else
  270.         (void)sprintf(buff, "%s:%d:%s:%ld",
  271.             MaxLength(p, p), cp->fd,
  272.             cp->Type == CTfile ? "file" : "proc", (long)pid);
  273.     break;
  274.     }
  275.     return buff;
  276. }
  277.  
  278.  
  279. /*
  280. **  Return the channel for a specified descriptor.
  281. */
  282. CHANNEL *
  283. CHANfromdescriptor(fd)
  284.     int        fd;
  285. {
  286.     if (fd <0 || fd > CHANtablesize)
  287.     return NULL;
  288.     return &CHANtable[fd];
  289. }
  290.  
  291.  
  292. /*
  293. **  Iterate over all channels of a specified type.
  294. */
  295. CHANNEL *
  296. CHANiter(ip, Type)
  297.     int            *ip;
  298.     CHANNELTYPE        Type;
  299. {
  300.     register CHANNEL    *cp;
  301.     register int    i;
  302.  
  303.     if ((i = *ip) >= 0 && i < CHANtablesize) {
  304.     do {
  305.         cp = &CHANtable[i];
  306.         if (Type == CTany || cp->Type == Type) {
  307.         *ip = ++i;
  308.         return cp;
  309.         }
  310.     } while (++i < CHANtablesize);
  311.     }
  312.     return NULL;
  313. }
  314.  
  315.  
  316. /*
  317. **  Mark a channel as an active reader.
  318. */
  319. void
  320. RCHANadd(cp)
  321.     register CHANNEL    *cp;
  322. {
  323.     FD_SET(cp->fd, &RCHANmask);
  324.     if (cp->fd > CHANlastfd)
  325.     CHANlastfd = cp->fd;
  326.  
  327.     /* Start reading at the beginning of the buffer. */
  328.     cp->In.Used = 0;
  329. }
  330.  
  331.  
  332. /*
  333. **  Remove a channel from the set of readers.
  334. */
  335. void
  336. RCHANremove(cp)
  337.     register CHANNEL    *cp;
  338. {
  339.     if (FD_ISSET(cp->fd, &RCHANmask)) {
  340.     FD_CLR(cp->fd, &RCHANmask);
  341.     if (cp->fd == CHANlastfd) {
  342.         /* This was the highest descriptor, get a new highest. */
  343.         while (!FD_ISSET(CHANlastfd, &RCHANmask)
  344.           && !FD_ISSET(CHANlastfd, &WCHANmask)
  345.           && CHANlastfd > 1)
  346.         CHANlastfd--;
  347.     }
  348.     }
  349. }
  350.  
  351.  
  352. /*
  353. **  Put a channel to sleep, call a function when it wakes.
  354. **  Note that the Argument must be NULL or allocated memory!
  355. */
  356. void
  357. SCHANadd(cp, Waketime, Event, Waker, Argument)
  358.     register CHANNEL    *cp;
  359.     time_t        Waketime;
  360.     POINTER        Event;
  361.     FUNCPTR        Waker;
  362.     POINTER        Argument;
  363. {
  364.     if (!FD_ISSET(cp->fd, &SCHANmask)) {
  365.     SCHANcount++;
  366.     FD_SET(cp->fd, &SCHANmask);
  367.     }
  368.     if (cp->fd > CHANlastsleepfd)
  369.     CHANlastsleepfd = cp->fd;
  370.     cp->Waketime = Waketime;
  371.     cp->Waker = Waker;
  372.     if (cp->Argument != Argument) {
  373.     DISPOSE(cp->Argument);
  374.     cp->Argument = Argument;
  375.     }
  376.     cp->Event = Event;
  377. }
  378.  
  379.  
  380. /*
  381. **  Take a channel off the sleep list.
  382. */
  383. void
  384. SCHANremove(cp)
  385.     register CHANNEL    *cp;
  386. {
  387.     if (FD_ISSET(cp->fd, &SCHANmask)) {
  388.     FD_CLR(cp->fd, &SCHANmask);
  389.     SCHANcount--;
  390.     cp->Waketime = 0;
  391.     if (cp->fd == CHANlastsleepfd) {
  392.         /* This was the highest descriptor, get a new highest. */
  393.         while (!FD_ISSET(CHANlastsleepfd, &WCHANmask)
  394.           && CHANlastsleepfd > 1)
  395.         CHANlastsleepfd--;
  396.     }
  397.     }
  398. }
  399.  
  400.  
  401. /*
  402. **  Is a channel on the sleep list?
  403. */
  404. BOOL
  405. CHANsleeping(cp)
  406.     CHANNEL    *cp;
  407. {
  408.     return FD_ISSET(cp->fd, &SCHANmask);
  409. }
  410.  
  411.  
  412. /*
  413. **  Wake up channels waiting for a specific event.
  414. */
  415. void
  416. SCHANwakeup(Event)
  417.     register POINTER    Event;
  418. {
  419.     register CHANNEL    *cp;
  420.     register int    i;
  421.  
  422.     for (cp = CHANtable, i = CHANtablesize; --i >= 0; cp++)
  423.     if (cp->Type != CTfree && cp->Event == Event && CHANsleeping(cp))
  424.         cp->Waketime = 0;
  425. }
  426.  
  427.  
  428. /*
  429. **  Mark a channel as an active writer.  Don't reset the Out->Left field
  430. **  since we could have buffered I/O already in there.
  431. */
  432. void
  433. WCHANadd(cp)
  434.     register CHANNEL    *cp;
  435. {
  436.     if (cp->Out.Left > 0) {
  437.     FD_SET(cp->fd, &WCHANmask);
  438.     if (cp->fd > CHANlastfd)
  439.         CHANlastfd = cp->fd;
  440.     }
  441. }
  442.  
  443.  
  444. /*
  445. **  Remove a channel from the set of writers.
  446. */
  447. void
  448. WCHANremove(cp)
  449.     register CHANNEL    *cp;
  450. {
  451.     if (FD_ISSET(cp->fd, &WCHANmask)) {
  452.     FD_CLR(cp->fd, &WCHANmask);
  453.     if (cp->Out.Left <= 0) {
  454.         /* No data left -- reset used so we don't grow the buffer. */
  455.         cp->Out.Used = 0;
  456.         cp->Out.Left = 0;
  457.     }
  458.     if (cp->fd == CHANlastfd) {
  459.         /* This was the highest descriptor, get a new highest. */
  460.         while (!FD_ISSET(CHANlastfd, &RCHANmask)
  461.           && !FD_ISSET(CHANlastfd, &WCHANmask)
  462.           && CHANlastfd > 1)
  463.         CHANlastfd--;
  464.     }
  465.     }
  466. }
  467.  
  468.  
  469. /*
  470. **  Set a channel to start off with the contents of an existing channel.
  471. */
  472. void
  473. WCHANsetfrombuffer(cp, bp)
  474.     CHANNEL    *cp;
  475.     BUFFER    *bp;
  476. {
  477.     WCHANset(cp, &bp->Data[bp->Used], bp->Left);
  478. }
  479.  
  480.  
  481.  
  482. /*
  483. **  Read in text data, return the amount we read.
  484. */
  485. int
  486. CHANreadtext(cp)
  487.     register CHANNEL    *cp;
  488. {
  489.     register int    i;
  490.     register BUFFER    *bp;
  491.     char        *p;
  492.  
  493.     p = CHANname(cp);
  494.  
  495.     /* Read in whatever is there. */
  496.     bp = &cp->In;
  497.     bp->Left = bp->Size - bp->Used;
  498.     i = read(cp->fd, &bp->Data[bp->Used], bp->Left - 1);
  499.     if (i < 0) {
  500.     syslog(L_ERROR, "%s cant read %m", p);
  501.     return -1;
  502.     }
  503.     if (i == 0) {
  504.     syslog(L_NOTICE, "%s readclose", p);
  505.     CHANclose(cp, p);
  506.     return 0;
  507.     }
  508.  
  509.     /* Update values, grow buffer if we're getting close. */
  510.     bp->Used += i;
  511.     bp->Left -= i;
  512.     if (bp->Left <= LOW_WATER) {
  513.     i = GROW_AMOUNT(bp->Size);
  514.     bp->Size += i;
  515.     bp->Left += i;
  516.     RENEW(bp->Data, char, bp->Size);
  517.     }
  518.     return i;
  519. }
  520.  
  521.  
  522. /*
  523. **  If I/O backs up a lot, we can get EMSGSIZE on some systems.  If that
  524. **  happens we want to do the I/O in chunks.  We assume stdio's BUFSIZ is
  525. **  a good chunk value.
  526. */
  527. STATIC int
  528. largewrite(fd, p, length)
  529.     register int    fd;
  530.     register char    *p;
  531.     register int    length;
  532. {
  533.     register int    i;
  534.     register char    *save;
  535.  
  536.     do {
  537.     /* Try the standard case -- write it all. */
  538.     i = write(fd, (POINTER)p, (SIZE_T)length);
  539.     if (i > 0 || (i < 0 && errno != EMSGSIZE && errno != EINTR))
  540.         return i;
  541.     } while (i < 0 && errno == EINTR);
  542.  
  543.     /* Write it in pieces. */
  544.     for (save = p, i = 0; length; p += i, length -= i) {
  545.     i = write(fd, (POINTER)p, (SIZE_T)(length > BUFSIZ ? BUFSIZ : length));
  546.     if (i <= 0)
  547.         break;
  548.     }
  549.  
  550.     /* Return error, or partial results if we got something. */
  551.     return p == save ? i : p - save;
  552. }
  553.  
  554.  
  555. /*
  556. **  Try to flush out the buffer.  Use this only on file channels!
  557. */
  558. BOOL
  559. WCHANflush(cp)
  560.     register CHANNEL    *cp;
  561. {
  562.     register BUFFER    *bp;
  563.     register int    i;
  564.  
  565.     /* If nothing in there, or nothing left, nothing to do. */
  566.     bp = &cp->Out;
  567.     if (bp->Left == 0)
  568.     return TRUE;
  569.  
  570.     /* Write it. */
  571.     while (bp->Left > 0) {
  572.     i = write(cp->fd, (POINTER)&bp->Data[bp->Used], (SIZE_T)bp->Left);
  573.     if (i < 0) {
  574.         syslog(L_ERROR, "%s cant flush count %d %m",
  575.         CHANname(cp), bp->Left);
  576.         return FALSE;
  577.     }
  578.     if (i == 0) {
  579.         syslog(L_ERROR, "%s cant flush count %d",
  580.         CHANname(cp), bp->Left);
  581.         return FALSE;
  582.     }
  583.     bp->Left -= i;
  584.     bp->Used += i;
  585.     if (bp->Left <= 0)
  586.         WCHANremove(cp);
  587.     }
  588.     return TRUE;
  589. }
  590.  
  591.  
  592.  
  593. /*
  594. **  Wakeup routine called after a write channel was put to sleep.
  595. */
  596. STATIC FUNCTYPE
  597. CHANwakeup(cp)
  598.     CHANNEL    *cp;
  599. {
  600.     syslog(L_NOTICE, "%s wakeup", CHANname(cp));
  601.     WCHANadd(cp);
  602. }
  603.  
  604.  
  605. /*
  606. **  Attempting to write would block; stop output or give up.
  607. */
  608. STATIC void
  609. CHANwritesleep(cp, p)
  610.     register CHANNEL    *cp;
  611.     char        *p;
  612. {
  613.     int            i;
  614.  
  615.     if ((i = ++(cp->BlockedWrites)) > BAD_IO_COUNT)
  616.     switch (cp->Type) {
  617.     default:
  618.         break;
  619.     case CTnntp:
  620.     case CTfile:
  621.     case CTexploder:
  622.     case CTprocess:
  623.         syslog(L_ERROR, "%s blocked closing", p);
  624.         SITEchanclose(cp);
  625.         CHANclose(cp, p);
  626.         return;
  627.     }
  628.     i *= BLOCK_BACKOFF;
  629.     syslog(L_ERROR, "%s blocked sleeping %d", p, i);
  630.     SCHANadd(cp, (time_t)(Now.time + i), (POINTER)NULL,
  631.     CHANwakeup, (POINTER)NULL);
  632. }
  633.  
  634.  
  635. #if    defined(INND_FIND_BAD_FDS)
  636. /*
  637. **  We got an unknown error in select.  Find out the culprit.
  638. **  Not really ready for production use yet, and it's expensive, too.
  639. */
  640. STATIC void
  641. CHANdiagnose()
  642. {
  643.     FDSET        Test;
  644.     int            i;
  645.     struct timeval     t;
  646.  
  647.     FD_ZERO(&Test);
  648.     for (i = CHANlastfd; i >= 0; i--) {
  649.     if (FD_ISSET(i, &RCHANmask)) {
  650.         FD_SET(i, &Test);
  651.         t.tv_sec = 0;
  652.         t.tv_usec = 0;
  653.         if (select(i + 1, &Test, (FDSET *)NULL, (FDSET *)NULL, &t) < 0
  654.           && errno != EINTR) {
  655.         syslog(L_ERROR, "%s Bad Read File %d", LogName, i);
  656.         FD_CLR(i, &RCHANmask);
  657.         /* Probably do something about the file descriptor here; call
  658.          * CHANclose on it? */
  659.         }
  660.         FD_CLR(i, &Test);
  661.     }
  662.     if (FD_ISSET(i, &WCHANmask)) {
  663.         FD_SET(i, &Test);
  664.         t.tv_sec = 0;
  665.         t.tv_usec = 0;
  666.         if (select(i + 1, (FDSET *)NULL, &Test, (FDSET *)NULL, &t) < 0
  667.          && errno != EINTR) {
  668.         syslog(L_ERROR, "%s Bad Write File %d", LogName, i);
  669.         FD_CLR(i, &WCHANmask);
  670.         /* Probably do something about the file descriptor here; call
  671.          * CHANclose on it? */
  672.         }
  673.         FD_CLR(i, &Test);
  674.     }
  675.     }
  676. }
  677. #endif    /* defined(INND_FIND_BAD_FDS) */
  678.  
  679.  
  680. /*
  681. **  Main I/O loop.  Wait for data, call the channel's handler when there is
  682. **  something to read or when the queued write is finished.  In order to
  683. **  be fair (i.e., don't always give descriptor n priority over n+1), we
  684. **  remember where we last had something and pick up from there.
  685. */
  686. void
  687. CHANreadloop()
  688. {
  689.     static char        EXITING[] = "INND exiting because of signal\n";
  690.     static int        fd;
  691.     register int    i;
  692.     register int    startpoint;
  693.     register int    count;
  694.     register int    lastfd;
  695.     int            oerrno;
  696.     register CHANNEL    *cp;
  697.     register BUFFER    *bp;
  698.     FDSET        MyRead;
  699.     FDSET        MyWrite;
  700.     struct timeval    MyTime;
  701.     long        silence;
  702.     char        *p;
  703.  
  704.     for ( ; ; ) {
  705.     /* See if any processes died. */
  706.     PROCscan();
  707.  
  708.     /* Wait for data, note the time. */
  709.     MyRead = RCHANmask;
  710.     MyWrite = WCHANmask;
  711.     MyTime = TimeOut;
  712.     count = select(CHANlastfd + 1, &MyRead, &MyWrite, (FDSET *)NULL,
  713.         &MyTime);
  714.     if (GotTerminate) {
  715.         (void)write(2, EXITING, STRLEN(EXITING));
  716.         CleanupAndExit(0, "received signal");
  717.     }
  718.     if (count < 0) {
  719.         if (errno != EINTR) {
  720.         syslog(L_ERROR, "%s cant select %m", LogName);
  721. #if    defined(INND_FIND_BAD_FDS)
  722.         CHANdiagnose();
  723. #endif    /* defined(INND_FIND_BAD_FDS) */
  724.         }
  725.         continue;
  726.     }
  727.  
  728.     if (count == 0) {
  729.         /* No channels active, so flush and skip if nobody's
  730.          * sleeping. */
  731.         if (Mode == OMrunning)
  732.         ICDwrite();
  733.         if (SCHANcount == 0)
  734.         continue;
  735.     }
  736.  
  737.     /* Update the "reasonably accurate" time. */
  738.     if (GetTimeInfo(&Now) < 0)
  739.         syslog(L_ERROR, "%s cant gettimeinfo %m", LogName);
  740.  
  741.     /* Try the control channel first. */
  742.     if (FD_ISSET(CHANccfd, &RCHANmask) && FD_ISSET(CHANccfd, &MyRead)) {
  743.         count--;
  744.         (*CHANcc->Reader)(CHANcc);
  745.         FD_CLR(CHANccfd, &MyRead);
  746.     }
  747.  
  748.     /* Loop through all active channels.  Somebody could have closed
  749.      * closed a channel so we double-check the global mask before
  750.      * looking at what select returned.  The code here is written so
  751.      * that a channel could be reading and writing and sleeping at the
  752.      * same time, even though that's not possible.  (Just as well,
  753.      * since in SysVr4 the count would be wrong.) */
  754.     lastfd = CHANlastfd;
  755.     if (lastfd < CHANlastsleepfd)
  756.         lastfd = CHANlastsleepfd;
  757.     if (fd > lastfd)
  758.         fd = 0;
  759.     startpoint = fd;
  760.     do {
  761.         cp = &CHANtable[fd];
  762.         if (cp->Type == CTfree)
  763.         goto Next;
  764.  
  765.         /* Anything to read? */
  766.         if (FD_ISSET(fd, &RCHANmask) && FD_ISSET(fd, &MyRead)) {
  767.         count--;
  768.         cp->LastActive = Now.time;
  769.         (*cp->Reader)(cp);
  770.         }
  771.  
  772.         /* Possibly recheck for dead children so we don't get SIGPIPE
  773.          * on readerless channels. */
  774.         if (PROCneedscan)
  775.         PROCscan();
  776.  
  777.         /* Ready to write? */
  778.         if (FD_ISSET(fd, &WCHANmask) && FD_ISSET(fd, &MyWrite)) {
  779.         count--;
  780.         bp = &cp->Out;
  781.         if (bp->Left) {
  782.             cp->LastActive = Now.time;
  783.             i = largewrite(fd, &bp->Data[bp->Used], bp->Left);
  784.             if (i <= 0) {
  785.             oerrno = errno;
  786.             p = CHANname(cp);
  787.             errno = oerrno;
  788.             if (i < 0)
  789.                 syslog(L_ERROR, "%s cant write %m", p);
  790.             else
  791.                 syslog(L_ERROR, "%s cant write", p);
  792.             cp->BadWrites++;
  793.             if (i < 0 && oerrno == EPIPE) {
  794.                 SITEchanclose(cp);
  795.                 CHANclose(cp, p);
  796.             }
  797.             else if (i < 0 && oerrno == EWOULDBLOCK) {
  798.                 WCHANremove(cp);
  799.                 CHANwritesleep(cp, p);
  800.             }
  801.             else if (cp->BadWrites >= BAD_IO_COUNT) {
  802.                 syslog(L_ERROR, "%s sleeping", p);
  803.                 WCHANremove(cp);
  804.                 SCHANadd(cp, (time_t)(Now.time + PAUSE_RETRY_TIME),
  805.                 (POINTER)NULL, CHANwakeup, (POINTER)NULL);
  806.             }
  807.             }
  808.             else {
  809.             cp->BadWrites = 0;
  810.             cp->BlockedWrites = 0;
  811.             bp->Left -= i;
  812.             bp->Used += i;
  813.             if (bp->Left <= 0) {
  814.                 WCHANremove(cp);
  815.                 (*cp->WriteDone)(cp);
  816.             }
  817.             }
  818.         }
  819.         }
  820.  
  821.         /* Coming off a sleep? */
  822.         if (FD_ISSET(fd, &SCHANmask) && cp->Waketime <= Now.time) {
  823.         cp->LastActive = Now.time;
  824.         SCHANremove(cp);
  825.         (*cp->Waker)(cp);
  826.         }
  827.  
  828.         /* Has this channel been inactive very long? */
  829.         if (cp->Type == CTnntp
  830.          && cp->LastActive + cp->NextLog < Now.time) {
  831.         p = CHANname(cp);
  832.         silence = Now.time - cp->LastActive;
  833.         cp->NextLog += CHANNEL_INACTIVE_TIME;
  834.         syslog(L_NOTICE, "%s inactive %ld", p, silence / 60L);
  835.         if (silence > PEER_TIMEOUT) {
  836.             syslog(L_NOTICE, "%s timeout", p);
  837.             CHANclose(cp, p);
  838.         }
  839.         }
  840.  
  841.     Next:
  842.         /* Bump pointer, modulo the table size. */
  843.         if (fd >= lastfd)
  844.         fd = 0;
  845.         else
  846.         fd++;
  847.  
  848.         /* If there is nothing to do, break out. */
  849.         if (count == 0 && SCHANcount == 0)
  850.         break;
  851.  
  852.     } while (fd != startpoint);
  853.     }
  854. }
  855.